#
# author: BisonLeo

import math
import numpy as np


class MeanVariance(object):
    def __init__(self):
        self.m2 = self.avg = 0.0
        self.maxnum = self.minnum = None
        self.num = 0

    def clear(self):
        self.m2 = self.avg = 0.0
        self.minnum = self.maxnum = None
        self.num = 0

    def reset(self, data=None):
        self.clear()
        if not data is None:
            self.add(data)

    def add(self, val):
        self.num += 1
        delta = val - self.avg
        self.avg += delta/self.num
        self.m2 += delta*(val-self.avg)
        if self.maxnum is None or val > self.maxnum:
            self.maxnum = val
        if self.minnum is None or val < self.minnum:
            self.minnum = val

    def variance(self):
        if self.num < 2:
            return 0
        return self.m2/(self.num-1)

    def std(self):
        return math.sqrt(math.fabs(self.variance()))

    def mean(self):
        return self.avg


class MeanVarianceWindow(MeanVariance):
    def __init__(self, wsize):
        super(MeanVarianceWindow, self).__init__()
        self.wsize = wsize
        self.pos = self.num = 0
        self.data = [0.0]*wsize

    def clear(self):
        mn = self.mean()
        self.pos = self.num = 0
        self.data = [mn, ]*self.wsize

    def add(self, val):
        self.num += 1
        oavg = self.avg
        xold = self.data[self.pos]

        # reaches end?
        if self.num > self.wsize:
            self.num = self.wsize
            self.avg += (val-xold)/self.num
            self.m2 += (val+xold-oavg-self.avg)*(val-xold)
        else:
            self.avg += (val - self.avg)/self.num
            self.m2 += (val - oavg)*(val - self.avg)
        self.data[self.pos] = val
        self.pos += 1
        if self.pos >= self.wsize:
            self.pos = 0
        if self.maxnum is None or val > self.maxnum:
            self.maxnum = val
        if self.minnum is None or val < self.minnum:
            self.minnum = val

    def median(self):
        return np.median(np.array(self.data))


if __name__ == "__main__":
    import math
    import numpy as np
    from scipy.io import wavfile
    import matplotlib.pyplot as plt
    filename = 'test1.wav'
    sample_rate, signal = wavfile.read(filename)
    signal = signal[:, 0]

    mv = MeanVariance()
    mvw = MeanVarianceWindow(20)
    meanofstd = MeanVarianceWindow(20)
    mvwofsd = MeanVarianceWindow(20)
    meanofstd = MeanVarianceWindow(20)
    A = []
    B = []
    C = []
    D = []
    for i in range(0,len(signal)):
        try:
            mv.add(signal[i])
            mvw.add(signal[i])
            #print("signal ",i, "=", signal[i],"\tmvw.mean()=",mvw.mean())
            A.append(mvw.mean())
        except RuntimeWarning as err:
            print("warning at:", i)
        mvwofsd.add(signal[i] - mvw.mean())
        meanofstd.add(mvwofsd.std())
        """ C.append(mvwofsd.std())
        D.append(meanofstd.mean()) """

    print(mv.mean(), mv.variance())
    print(mvw.mean(), mvw.variance())
    
    plt.subplot(211)
    plt.plot(A,label="running average for 10 points")
    #plt.plot(B,label="running average")
    #plt.plot(C,label="running std_mvw")
    #plt.plot(D,label="running mean of std")
    plt.subplot(212)
    plt.plot(signal,label="signal")
    plt.legend()
    plt.show()